#pragma once
#include "Socket.hpp"
#include "Log.hpp"
#include<sys/select.h>
using namespace SocketModule;

using namespace LogModule;
// using namespace SocketModule;

class SelectServer
{
    const static int size = sizeof(fd_set) * 8;
    const static int defaultfd = -1;

public:
    //根据端口构建本地TCP服务
    SelectServer(int port) : _listensock(std::make_unique<TcpSocket>()), _isrunning(false)
    {
        _listensock->BuildTcpSocketMethod(port);
        for (int i = 0; i < size; i++)
        {
            _fd_array[i] = defaultfd;
        }
        _fd_array[0] = _listensock->Fd();
    }

    //开始监听套接字,套接字有数据则执行后续操作
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            fd_set rfds;
            //将套接字清空
            FD_ZERO(&rfds);
            int maxfd = defaultfd;
            //重新设置新的套接字
            for (int i = 0; i < size; i++)
            {
                if (_fd_array[i] == defaultfd)
                {
                    continue;
                }
                FD_SET(_fd_array[i], &rfds);
                if (maxfd < _fd_array[i])
                {
                    maxfd = _fd_array[i];
                }
            }
            //打印所有新建立的套接字
            PrintFd();
            //监听rfds里面对应的套接字,select会将rfds修改,将有监听信号的套接字设置为1
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);

            switch (n)
            {
            case -1:
                LOG(LogLevel::ERROR) << "SELECT ERROR";
                break;
            case 0:
                LOG(LogLevel::DEBUG) << "TIME OUT...";
                break;
            default:
                LOG(LogLevel::DEBUG) << "有事件就绪了....,n:" << n;
                Dispatcher(rfds);
                break;
            }
        }
        _isrunning=false;
    }
    //对每个每个套接字进行数据读取，如果套接字3有数据则Accepter()新建立一个套接字
    void Dispatcher(fd_set& rfds)
    {
        for(int i=0;i<size;i++)
        {
            if(_fd_array[i]==defaultfd)
            {
                continue;
            }
            if(FD_ISSET(_fd_array[i],&rfds))
            {
                //用于监听新的连接的套接字有信号则建立新的套接字
                if(_fd_array[i]==_listensock->Fd())
                {
                    //建立新的连接Accepter中封装了Accept,Accept里面封装了accept,返回sockaddr_in,里面包含客户端的IP和端口
                    Accepter();
                }
                else
                {
                    //读取对应套接字的数据,即使客户端断开连接也会有一个可读信号
                    Recver(_fd_array[i],i);
                }
            }
        }
    }
    void Accepter()
    {
        InetAddr client;
        //返回新连接的客户端所对应的套接字
        int sockfd=_listensock->Accept(&client);
        if(sockfd>=0)
        {
            LOG(LogLevel::INFO)<<"GET A NEW LINK,SOCKFD:"<<sockfd<<",client is:"<<client.StringAddr();
            int pos=0;
            //在空余的套接字位置创建新的套接字
            for(;pos<size;pos++)
            {
                if(_fd_array[pos]==defaultfd)
                {
                    break;
                }
            }
            //套接字使用完
            if(pos==size)
            {
                LOG(LogLevel::WARNING)<<"SELECT SERVER FULL";
                close(sockfd);
            }
            //创建新的套接字
            else
            {
                _fd_array[pos]=sockfd;
            }
        }
    }
    void Recver(int fd,int pos)
    {
        char buffer[1024];
        //将套接字里面的数据保存在buffer中并且打印出来
        ssize_t n=recv(fd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n]=0;
            std::cout<<"ClIENT SAY@"<<buffer<<std::endl;
        }
        //n==0代表连接超时
        else if(n==0)
        {
            LOG(LogLevel::INFO)<<"CLIENT QUIT....";
            _fd_array[pos]=defaultfd;
        }
        //其它则表示连接错误
        else
        {
            LOG(LogLevel::ERROR)<<"RECV ERROR";
            _fd_array[pos]=defaultfd;
            close(fd);
        }
    }

    //打印套接字
    void PrintFd()
    {
        LOG(LogLevel::INFO)<<"_fd_array[]:";
        for(int i=0;i<size;i++)
        {
            if(_fd_array[i]==defaultfd)
            {
                continue;
            }
            LOG(LogLevel::INFO)<<_fd_array[i]<<" ";
        }
        LOG(LogLevel::INFO)<<"\r\n";
    }
    //暂停while死循环服务
    void Stop()
    {
        _isrunning=false;
    }
    ~SelectServer()
    {

    }
private:
    std::unique_ptr<Socket> _listensock;//用于建立本地服务端的TCP服务,并且指向套接字3
    bool _isrunning;//标志位
    int _fd_array[size];//存储新建立连接的套接字
};
